#pragma once

#include <random>
#include <cstdint>
#include "../Utils/PlatTypeUtil.h"

class Random32
{
public:
	inline Random32()
		: mGenerationCount(0)
	{
	}
	inline Random32(uint32 seed)
		: mEngine(seed)
		, mGenerationCount(0)
	{
	}
	inline Random32(uint32 seed, uint64 skip)
		: mEngine(seed)
		, mGenerationCount(skip)
	{
		mEngine.discard(skip);
	}

	inline float operator()(float min, float max)
	{
		++mGenerationCount;
		std::uniform_real_distribution<float> realDist(min, max);
		return realDist(mEngine);
	}

	inline int32_t operator()(int32_t min, int32_t max)
	{
		++mGenerationCount;
		std::uniform_int_distribution<int32_t> intDist(min, max);
		return intDist(mEngine);
	}

	inline uint32 operator()(uint32 min, uint32 max)
	{
		++mGenerationCount;
		std::uniform_int_distribution<uint32> uintDist(min, max);
		return uintDist(mEngine);
	}

	inline uint64 GetNumbersGenerated() const
	{
		return mGenerationCount;
	}

	inline void Discard(uint64 steps)
	{
		mEngine.discard(steps);
		mGenerationCount += steps;
	}

	static uint32 RandSeed();
	static Random32 Rand;

private:
	std::mt19937 mEngine;

	uint64 mGenerationCount;
};

class Random64
{
public:
	Random64()
		: mGenerationCount(0)
	{
	}
	Random64(uint64 seed)
		: mEngine(seed)
		, mGenerationCount(0)
	{
	}
	Random64(uint64 seed, uint64 skip)
		: mEngine(seed)
		, mGenerationCount(skip)
	{
		mEngine.discard(skip);
	}

	inline float operator()(float min, float max)
	{
		++mGenerationCount;
		std::uniform_real_distribution<float> realDist(min, max);
		return realDist(mEngine);
	}

	inline double operator()(double min, double max)
	{
		++mGenerationCount;
		std::uniform_real_distribution<double> realDist(min, max);
		return realDist(mEngine);
	}

	inline int64 operator()(int64 min, int64 max)
	{
		++mGenerationCount;
		std::uniform_int_distribution<int64> intDist(min, max);
		return intDist(mEngine);
	}

	inline uint64 operator()(uint64 min, uint64 max)
	{
		++mGenerationCount;
		std::uniform_int_distribution<uint64> uintDist(min, max);
		return uintDist(mEngine);
	}

	inline uint64 GetNumbersGenerated() const
	{
		return mGenerationCount;
	}

	inline void Discard(uint64 steps)
	{
		mEngine.discard(steps);
		mGenerationCount += steps;
	}

	static uint64 RandSeed();
	static Random64 Rand;

private:
	std::mt19937_64 mEngine;
	uint64 mGenerationCount;
};
